package com.wondream.myframework.app.common.aspect;

import com.alibaba.fastjson.JSON;
import com.wondream.myframework.app.common.annotation.CfssLogOffAnno;
import com.wondream.myframework.app.common.annotation.ESA;
import com.wondream.myframework.app.common.exception.BusinessException;
import com.wondream.myframework.app.common.exception.BusinessExeceptionCode;
import com.wondream.myframework.app.common.log.Log;
import com.wondream.myframework.app.common.util.JacksonUtil;
import com.wondream.myframework.app.vo.base.OpRequest;
import com.wondream.myframework.app.vo.base.OpResponse;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.xml.validation.Validator;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 请求响应切面
 */
@Slf4j
@Aspect
public class ReqRspAspect {
    @Resource
    private Validator validator;

    @Autowired(required = false)
    private CustomReqRspExtend customReqRspExtend;

//    @Resource
//    private CommonProperties commonProperties;

    /**
     * 切入点
     */
    @Pointcut("execution(public * com.wondream.myframework.app.controller..*.*(..))")
    public void pointCut(){

    }

    @Around("pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();
        ESA esaAnno = AnnotationUtils.findAnnotation(method, ESA.class);
        if (esaAnno!=null) {
            return esaAroundAdvice(pjp);
        }
        RequestMapping httpAnno = AnnotationUtils.findAnnotation(method, RequestMapping.class);
        if (httpAnno!=null) {
            return httpAroundAdvice(pjp);
        }
        return pjp.proceed();
    }

    private Object esaAroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();
        Object[] arguments = pjp.getArgs();
        CfssLogOffAnno logOffAnno = AnnotationUtils.findAnnotation(method, CfssLogOffAnno.class);
        ESA esaAnno = AnnotationUtils.findAnnotation(method, ESA.class);
        if (null==logOffAnno||logOffAnno.requestLog()) {
            log.info(Log.op("ReqRspAspect.esaAroundAdvice").msg("【esa请求】{}").kv("arguments", arguments).toString());
        }
        Object result = null;
        if (null != customReqRspExtend) {
            arguments = customReqRspExtend.transferArg(method, arguments);
        }else{
            arguments = transferArgByDefault(method, arguments);
        }
        try {
            result = pjp.proceed();
        } catch (Exception e) {
            OpResponse<String> model = new OpResponse<>();
            if(e instanceof BusinessException){
                BusinessException be = (BusinessException)e;
                model.setResponseCode(be.getResponseCode());
                model.setResponseMsg(be.getResponseMsg());
            } else {
                model.setResponseMsg(BusinessExeceptionCode.SYS_ERROR.getMsg());
                model.setResponseCode(BusinessExeceptionCode.SYS_ERROR.getCode());
            }
            result = model;
        } finally {
            if (null==logOffAnno||logOffAnno.requestLog()) {
                log.info(Log.op("ReqRspAspect.esaAroundAdvice").msg("【esa请求结果】{}").kv("result", result).toString());
            }
        }
        return result;
    }

    private Object[] transferArgByDefault(Method method, Object[] arguments) {
        Type[] types = method.getGenericParameterTypes();
        Type type = types[0];
        if (type instanceof ParameterizedType) {
            Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
            Type t = actualTypes[0];
            Object arg = arguments[0];
            if (OpRequest.class.isAssignableFrom(arg.getClass())) {
                OpRequest request = (OpRequest) arg;
                Object o = JSON.parseObject(JacksonUtil.toJson(request.getParams()), (Class) t);
                request.setParams(o);
                arguments[0] = request;
            }
        }
        return arguments;
    }

    private Object httpAroundAdvice(ProceedingJoinPoint pjp) {
        long start = System.currentTimeMillis();
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();
        ServletRequestAttributes sra = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sra.getRequest();
        Object[] arguments = pjp.getArgs();
        CfssLogOffAnno logOffAnno = AnnotationUtils.findAnnotation(method, CfssLogOffAnno.class);
        if (null==logOffAnno||logOffAnno.requestLog()) {
            log.info(Log.op("ReqRspAspect.httpAroundAdvice").msg("【http请求】{}").kv("arguments", arguments)
                    .kv("method", request.getMethod()).kv("requestURI", request.getRequestURI()).toString());
        }
        Object result = null;
        OpResponse<String> model = new OpResponse<>();
        model.setResponseMsg(BusinessExeceptionCode.SYS_ERROR.getMsg());
        model.setResponseCode(BusinessExeceptionCode.SYS_ERROR.getCode());
        try {
            result = pjp.proceed();
        } catch (Exception e) {
            if(e instanceof BusinessException){
                BusinessException be = (BusinessException)e;
                model.setResponseCode(be.getResponseCode());
                model.setResponseMsg(be.getResponseMsg());
            }
            result = model;
        } catch (Throwable throwable) {

        } finally {
            if (null==logOffAnno||logOffAnno.requestLog()) {
                log.info(Log.op("ReqRspAspect.esaAroundAdvice").msg("【esa请求结果】{}").kv("result", result).toString());
            }
        }
        return result;
    }
}
